package com.aboverock.common.helper;

import com.aboverock.common.constant.KbsmartConstant;
import com.aboverock.core.spring.SpringWebHolder;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.web.util.WebUtils;

/**
 * 分页实体帮助类.
 *
 * @author Rock Wang
 */
public class PageHelper {
    /**
     * 生成Spring Data分页类， {@link Pageable}.
     *
     * @return the pageable
     */
    public static Pageable pageable() {
        HttpServletRequest request = SpringWebHolder.getRequest();
        return pageable(request);
    }
    /**
     * 根据请求生成Spring Data分页类， {@link Pageable}.
     *
     * @param request must not be {@literal null}
     * @return the pageable
     */
    public static Pageable pageable(HttpServletRequest request) {

        int current = getIntPara(request, KbsmartConstant.Pagination.CURRNET_PARA_NAME,
                KbsmartConstant.Pagination.CURRENT_PAGE_DEFAULT);
        int pageSize = getIntPara(request, KbsmartConstant.Pagination.PAGE_SIZE_PARA_NAME,
                KbsmartConstant.Pagination.PAGE_SIZE_DEFAULT);

        /*
         * can be {@literal null}, and format must be column1,order OR column1,column2,order OR
         * column1,column3,order1;column2,order2
         */
        String sortValue = WebUtils.findParameterValue(request,
                KbsmartConstant.Pagination.SORT_PARA_NAME);

        return pageable(current, pageSize, sortValue);
    }
    
    /**
     * 根据参数生成Spring Data分页类， {@link Pageable}.
     *
     * @param current the current
     * @param pageSize the page size
     * @return the pageable
     */
    public static Pageable pageable(int current, int pageSize) {
        return pageable(current, pageSize, "");
    }

    /**
     * 根据参数生成Spring Data分页类， {@link Pageable}.
     *
     * @param current the current
     * @param pageSize the page size
     * @param sortValue can be {@literal null}, and format must be column1,order OR
     *            column1,column2,order OR column1,column3,order1;column2,order2
     * @return the pageable
     */
    public static Pageable pageable(int current, int pageSize, String sortValue) {
        Pageable pageable;
        if (StringUtils.isNotEmpty(sortValue)) {
            String[] sorts = StringUtils.split(sortValue, StringPool.SEMICOLON);

            List<Order> orderList = Arrays.stream(sorts).filter(StringUtils::isNotEmpty)
                    .map(sortExpr -> {
                        String[] orders = StringUtils.split(sortExpr, StringPool.COMMA);
                        Direction direction = Direction.fromString(orders[orders.length - 1]);
                        return Arrays.stream(orders).limit(orders.length - 1).map(orderProperty -> {
                            Order order;
                            if (direction.isAscending()) {
                                order = Sort.Order.asc(orderProperty);
                            } else {
                                order = Sort.Order.desc(orderProperty);
                            }
                            return order;
                        }).collect(Collectors.toList());
                    }).flatMap(List::stream).collect(Collectors.toList());

            pageable = PageRequest.of(current - 1, pageSize, Sort.by(orderList));
        } else {
            pageable = PageRequest.of(current - 1, pageSize);
        }

        return pageable;
    }

    /**
     * 根据给定参数生成mybatis-plus分页类， {@link Page}.
     *
     * @param <T> the generic type
     * @param current the current
     * @param pageSize the page size
     * @param clazz the clazz
     * @return the page
     */
    public static <T> Page<T> page(int current, int pageSize, Class<T> clazz) {
        return page(current, pageSize, "", clazz, false);
    }
    
    /**
     * 根据给定参数生成mybatis-plus分页类， {@link Page}.
     *
     * @param <T> the generic type
     * @param current the current
     * @param pageSize the page size
     * @param sortValue can be {@literal null}, and format must be column1,order OR
     *            column1,column2,order OR column1,column3,order1;column2,order2
     * @param clazz the clazz
     * @param convertMetadata if the convert java variable to table column
     * @return the page
     */
    public static <T> Page<T> page(int current, int pageSize, String sortValue, Class<T> clazz, boolean convertMetadata) {

        Page<T> page = new Page<>(current, pageSize);
        String[] ascs = {};
        String[] descs = {};
        HashMap<String, String[]> map = new HashMap<>(2);
        map.put("ascs", ascs);
        map.put("descs", descs);
        if (StringUtils.isNotEmpty(sortValue)) {
            String[] sorts = StringUtils.split(sortValue, StringPool.SEMICOLON);

            Arrays.stream(sorts).filter(StringUtils::isNotEmpty).forEach(sortExpr -> {
                String[] orders = StringUtils.split(sortExpr, StringPool.COMMA);
                Direction direction = Direction.fromString(orders[orders.length - 1]);
                if (direction.isAscending()) {
                    
                    String[] ascsTemp = Arrays.stream(orders).limit(orders.length - 1).map(
                        order -> convertMetadata ? MetadataHelper.convertVariable2Column(clazz, order) : order
                    ).toArray(String[]::new);
                    
                    map.put("ascs",
                            Stream.concat(Arrays.stream(map.get("ascs")), Arrays.stream(ascsTemp))
                                    .toArray(String[]::new));
                } else {
                    String[] descsTemp = Arrays.stream(orders).limit(orders.length - 1).map(
                        order -> convertMetadata ? MetadataHelper.convertVariable2Column(clazz, order) : order
                    ).toArray(String[]::new);
                    
                    map.put("descs",
                            Stream.concat(Arrays.stream(map.get("descs")), Arrays.stream(descsTemp))
                                    .toArray(String[]::new));
                }
            });
            page.setAsc(map.get("ascs"));
            page.setDesc(map.get("descs"));
        }
        return page;

    }

    /**
     * 根据请求生成mybatis-plus分页类， {@link Page}.
     *
     * @param <T> the generic type
     * @param request must not be {@literal null}
     * @param clazz the clazz
     * @param convertMetadata if the convert java variable to table column
     * @return the page
     */
    public static <T> Page<T> page(HttpServletRequest request, Class<T> clazz,
            boolean convertMetadata) {

        int current = getIntPara(request, KbsmartConstant.Pagination.CURRNET_PARA_NAME,
                KbsmartConstant.Pagination.CURRENT_PAGE_DEFAULT);
        int pageSize = getIntPara(request, KbsmartConstant.Pagination.PAGE_SIZE_PARA_NAME,
                KbsmartConstant.Pagination.PAGE_SIZE_DEFAULT);
        /*
         * can be {@literal null}, and format must be column1,order OR column1,column2,order OR
         * column1,column3,order1;column2,order2
         */
        String sortValue = WebUtils.findParameterValue(request,
                KbsmartConstant.Pagination.SORT_PARA_NAME);

        return page(current, pageSize, sortValue, clazz, convertMetadata);
    }
    
    /**
     * 生成mybatis-plus分页类， {@link Page}.
     *
     * @param <T> the generic type
     * @param clazz the clazz
     * @param convertMetadata if the convert java variable to table column
     * @return the page
     */
    public static <T> Page<T> page(Class<T> clazz, boolean convertMetadata) {
        HttpServletRequest request = SpringWebHolder.getRequest();
        return page(request, clazz, convertMetadata);
    }
    
    /**
     * 生成mybatis-plus分页类， {@link Page}
     * <BR>默认自动转换java bean的本地变量名数据库表栏位名
     *
     * @param <T> the generic type
     * @param clazz the clazz
     * @return the page
     */
    public static <T> Page<T> page(Class<T> clazz) {
        return page(clazz, true);
    }
    
    /**
     * 获取整型数据.
     *
     * @param request must not be {@literal null}
     * @param paraName the para name
     * @return the int para
     */
    private static int getIntPara(HttpServletRequest request, String paraName, int defaultValue) {
        String paraValue = WebUtils.findParameterValue(request, paraName);
        if (StringUtils.isEmpty(paraValue)) {
            return defaultValue;
        }
        return Integer.parseInt(paraValue);
    }

}
